package com.kinyx.framework.file;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

import com.kinyx.framework.annotation.assist.CanNotNull;
import com.kinyx.framework.annotation.assist.CanNull;
import com.kinyx.framework.annotation.assist.ReturnNotNull;
import com.kinyx.framework.annotation.assist.Throws;
import com.kinyx.framework.business.KRandomUtils;
import com.kinyx.framework.enums.impl.ErrorCode0;
import com.kinyx.framework.enums.impl.RandomRange;
import com.kinyx.framework.exception.KRuntimeException;
import com.kinyx.framework.utils.KDateTimeUtils;
import com.kinyx.framework.utils.KStringUtils;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

/**
 * 文件工具类
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class KFileUtils {

	/**
	 * 校验文件（是否存在；是否为文件；）
	 * @param file 文件对象
	 */
	@Throws(at = "KFileUtils.checkFile(File)", value = "ErrorCode0.FileDoesNotExists", when = "!File.exists()")
	@Throws(at = "KFileUtils.checkFile(File)", value = "ErrorCode0.IsNotAFile", when = "!File.isFile()")
	public static void checkFile(@CanNotNull final File file) {
		if (!file.exists()) {
			throw new KRuntimeException(ErrorCode0.FileDoesNotExists).param("fileName", file.getAbsolutePath().replaceAll("\\\\", "/"));
		}
		if (!file.isFile()) {
			throw new KRuntimeException(ErrorCode0.IsNotAFile).param("fileName", file.getAbsolutePath().replaceAll("\\\\", "/"));
		}
	}

	/**
	 * 校验文件（是否可读）
	 * @param file 文件对象
	 */
	@Throws(at = "KFileUtils.checkReadable(File)", value = "ErrorCode0.FileIsNotReadable", when = "!File.canRead()")
	public static void checkReadable(@CanNotNull final File file) {
		if (!file.canRead()) {
			throw new KRuntimeException(ErrorCode0.FileIsNotReadable).param("fileName", file.getAbsolutePath().replaceAll("\\\\", "/"));
		}
	}

	/**
	 * 校验文件（是否可写）
	 * @param file 文件对象
	 */
	@Throws(at = "KFileUtils.checkWriteable(File)", value = "ErrorCode0.FileIsNotWriteable", when = "!File.canWrite()")
	public static void checkWriteable(@CanNotNull final File file) {
		if (!file.canWrite()) {
			throw new KRuntimeException(ErrorCode0.FileIsNotWriteable).param("fileName", file.getAbsolutePath().replaceAll("\\\\", "/"));
		}
	}

	/**
	 * 校验目录（是否存在；是否为目录；）
	 * @param directory 目录对戏那个
	 * @param exceptionOrCreate true：目录不存在时抛出异常；false：目录不存在时创建目录；
	 */
	@Throws(at = "KFileUtils.checkDirectory(File, boolean)", value = "ErrorCode0.DirectoryDoesNotExists", when = "exceptionOrCreate == true && !File.exists()")
	@Throws(at = "KFileUtils.checkDirectory(File, boolean)", value = "ErrorCode0.IsNotADirectory", when = "!File.isDirectory()")
	public static void checkDirectory(@CanNotNull final File directory, @CanNotNull final boolean exceptionOrCreate) {
		if (!directory.exists()) {
			if (exceptionOrCreate) {
				throw new KRuntimeException(ErrorCode0.DirectoryDoesNotExists).param("directoryName", directory.getAbsolutePath().replaceAll("\\\\", "/"));
			}
			directory.mkdirs();
		}
		if (!directory.isDirectory()) {
			throw new KRuntimeException(ErrorCode0.IsNotADirectory).param("directoryName", directory.getAbsolutePath().replaceAll("\\\\", "/"));
		}
	}

	/**
	 * 转换为标准扩展名
	 * @param extension 扩展名
	 * @return 扩展名（大写）
	 */
	@ReturnNotNull
	public static String toExtension(@CanNull final String extension) {
		if (KStringUtils.isBlank(extension)) { return ""; }
		return extension.startsWith(".") ? extension.toUpperCase() : "." + extension.toUpperCase();
	}

	/**
	 * 取扩展名
	 * @param fileName 文件名（全路径/相对路径）
	 * @return 扩展名（大写）
	 */
	@ReturnNotNull
	public static String getExtension(@CanNotNull final String fileName) {
		final int lastIndexOfDot = fileName.lastIndexOf(".");// 取最后一个点索引值
		if (lastIndexOfDot < 0) {// 没有扩展名
			return "";
		}
		return fileName.substring(lastIndexOfDot).toUpperCase();
	}

	/**
	 * 去除扩展名
	 * @param fileName 文件名（全路径/相对路径）
	 * @return 去除扩展名的文件名
	 */
	@ReturnNotNull
	public static String removeExtension(@CanNotNull final String fileName) {
		final int lastIndexOfDot = fileName.lastIndexOf(".");// 取最后一个点索引值
		if (lastIndexOfDot < 0) {// 没有扩展名
			return fileName;
		}
		return fileName.substring(0, lastIndexOfDot);
	}

	/**
	 * 判断是否为某类型文件
	 * @param fileName 文件名（全路径/相对路径）
	 * @param extensionX 待匹配扩展名
	 * @param extensionMore 更多待匹配扩展名
	 * @return true：匹配成功；false：匹配失败；
	 */
	@ReturnNotNull
	public static boolean isTypeOf(@CanNotNull final String fileName, @CanNotNull final String extensionX, @CanNull final String... extensionMore) {
		final String extension = getExtension(fileName);
		if (extension.equals(toExtension(extensionX))) { return true; }
		if (extensionMore != null) {
			for (final String extensionY : extensionMore) {
				if (extension.equals(toExtension(extensionY))) { return true; }
			}
		}
		return false;
	}

	/**
	 * 创建文件对象，如果文件不存在，则生成文件。
	 * @param fileName 文件名（全路径）
	 * @return 文件对象
	 */
	@ReturnNotNull
	@Throws(at = "KFileUtils.checkDirectory(File, boolean)", value = "ErrorCode0.IsNotADirectory", when = "!File.isDirectory()")
	@Throws(at = "KFileUtils.createNewFile(String)", value = "IOException", when = "File.createNewFile()")
	public static File createNewFile(@CanNotNull final String fileName) {
		final File file = new File(fileName);
		if (!file.exists()) {
			checkDirectory(file.getParentFile(), false);
			try {
				file.createNewFile();
			} catch (final IOException e) {
				throw new KRuntimeException(e);
			}
		}
		return file;
	}

	/**
	 * 创建文件（/${basePath}/yyyy/yyyyMM/yyyyMMdd/yyyyMMddHHmmssSSS${random6}${extension}）
	 * @param basePath 文件存储目录
	 * @param extension 扩展名
	 * @return 文件对象
	 */
	@ReturnNotNull
	@Throws(at = "KFileUtils.checkDirectory(File, boolean)", value = "ErrorCode0.IsNotADirectory", when = "!File.isDirectory()")
	@Throws(at = "KFileUtils.createNewFile(String)", value = "IOException", when = "File.createNewFile()")
	public static File createNewFile(@CanNotNull final String basePath, @CanNull(defaultValue = "") final String extension) {
		final String filePath = new StringBuilder(KPathUtils.toPath(basePath))//
				.append(KDateTimeUtils.now("/yyyy/yyyyMM/yyyyMMdd/yyyyMMddHHmmssSSS"))//
				.append(KRandomUtils.randomString(false, 6, RandomRange.LETTER_FULL_LOWERCASE))//
				.append(toExtension(extension))//
				.toString();
		return createNewFile(filePath);
	}

	/**
	 * 重命名文件并将文件移入到目录中
	 * @param file 文件对象
	 * @param directory 目录
	 * @param newName 新文件名
	 */
	@ReturnNotNull
	@Throws(at = "KFileUtils.checkFile(File)", value = "ErrorCode0.FileDoesNotExists", when = "!File.exists()")
	@Throws(at = "KFileUtils.checkFile(File)", value = "ErrorCode0.IsNotAFile", when = "!File.isFile()")
	@Throws(at = "KFileUtils.checkDirectory(File, boolean)", value = "ErrorCode0.IsNotADirectory", when = "!File.isDirectory()")
	@Throws(at = "KFileUtils.renameAndMove(File, File, String)", value = "IOException", when = "Files.move(Path, Path, CopyOption...)")
	public static void renameAndMove(@CanNotNull final File file, @CanNotNull final File directory, @CanNotNull final String newName) {
		checkFile(file);
		checkDirectory(directory, false);
		try {
			Files.move(file.toPath(), Paths.get(directory.getAbsolutePath() + File.separator + newName));
		} catch (final IOException e) {
			throw new KRuntimeException(e);
		}
	}

}
